{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", " R4.Cyber.10 - Cryptographie
\n", "
\n", "

Navigation dans la page

\n", "

\n", " Si c'est votre première visite dans ce TP, lisez attentivement chacun des points détaillé après ce paragraphe.
\n", " Si vous avez déjà commencer à travailler sur ce TP et que vous souhaiter vous déplacer rapidement dans une partie précise vous pouvez choisir la partie que vous souhaitez rejoindre ci-dessous.
\n", "

\n", " Menu de navigation (cliquez pour dérouler)\n", " \n", "
\n", "

\n", "
\n", " Technologie jupyter (cliquer pour dérouler)\n", "

\n", " La technologie jupyter permet d'exécuter du code python par un simple clique sur Executer ci-dessus.
\n", " Les morceaux de code de cette page sont interprétées case par case. Pour savoir quelle case a été interprétée avant une autre, il suffit de repérer le numéro devant la case.
\n", " Une fois qu'une case a été interprétée (=exécutée), la page garde en mémoire les variables et fonctions lues
\n", " La plateforme propose quelques outils de purge de la mémoire : \n", "

\n", "

\n", "
\n", "

SAUVEGARDER VOTRE TRAVAIL

\n", "

\n", " Pour ne pas perdre votre travail pensez à le sauvegarder régulièrement. Par défault, la sauvegarde par un clic sur la disquette en haut à gauche de page, ou par le racourci clavier classique ctrl+S\n", " est une sauvegarde en local, sur le serveur de jupyter. Vous pouvez et devez très régulièrement sauvegarder votre travail sur votre support personnel de sauvegarde (clef USB, se l'envoyer par mail etc). Ce faisant vous disposerez d'un fichier .ipynb (IPYthon NoteBook) qu'il vous suffira de recharger pour avancer. Après le rechargement assurez vous que les fonctionnalités anciennement developpées et variables utilisées sont bien dans la mémoire de la page (en rééxecutant les cases, ou plus rapidement par Kernel > Restart & Run All.

\n", "

A NOTER : vous pouvez travailler sur le tp (et tout autre fichier .ipynb) hors connexion en installant une version local du notebook de jupyter. Il faut que votre machine possède un interpreteur de python et que vous soyez connecter à internet.\n", "

    \n", "
  1. Lancer un terminal
  2. \n", "
  3. Taper la commande suivante : pip install jupyterlab
  4. \n", "
  5. Une fois l'installation terminée portez votre attention sur les dernières lignes affichées dans votre terminal vous invitant probablement à taper une ligne de commande pour faire une mise à jour
  6. \n", "
  7. Pour lancer notebook de jupyter, taper dans votre termial : jupyter notebook
  8. \n", "
  9. Votre simulateur de serveur est lancé. Il ne faut pas fermer votre terminal, auquel cas votre simulateur de serveur s'interompera. Suivez le lien indiqué dans les dernières lignes de votre terminal pour vous diriger vers votre espace local. L'interface se présente comme celle que vous trouverez sur le web. Votre travail sera cependant toujours enregistré et jamais perdu même si vous le consultez après plusieurs jours
  10. \n", "
\n", "

\n", "
\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", " Partie 1 : Du caractère ! \n", "
\n", "\n", "\n", "

\n", "

\n", " Menu de navigation (cliquez pour dérouler)\n", " \n", "
\n", "

" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

\n", " Pour apréhender correctement ce TP de cryptologie, revenons sur la notion de chaine de caractères. \n", "
\n", " Une chaine de caractère est une variable de type string délimitée par des guillemets simple ou double (touche 3 ou 4) \n", "

" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "X=\"Voici une chaîne de caractères.\"\n", "print(X)\n", "print(type(X))#La fonction 'type' permet d'afficher le type d'une variable\n", "\n", "X='Voici une chaîne de caractères.'\n", "print(X)\n", "print(type(X))#La fonction 'type' permet d'afficher le type d'une variable" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

Pour acceder à un caractère on utilise les crochets. Par exemple X[12] permet d'acceder au 13ième caractères. En effet, les caractères sont numérotés à partir de 0.

" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x=X[12]\n", "print(\"Le 13ième caractère de \\\"\", X, \"\\\" est\", x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

La fonction len permet de déterminer la longueur d'une chaine

" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "n=len(X)\n", "print(\"La longueur de la chaine \\\"\", X, \"\\\" est\", n)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

Ecrire une procédure qui prend en paramètre un entier n et une chaine de caractère txt et qui affiche le n-ième caractère de la chaine. La fonction devra afficher un message d'erreur si l'entier n est plus grand que la longueur du texte ou négatif.

" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def afficheCara(n, txt) : \n", " print(\"Le caractère est ???\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#Test\n", "afficheCara(10, \"Hey ! Ca va ?\")\n", "afficheCara(15, \"Hey ! Ca va ?\")\n", "afficheCara(00, \"Hey ! Ca va ?\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

A l'aide d'une boucle while affichez un par un les caractères de la chaine X.

" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": true }, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

Il est possible de parcourir une chaine de caractère à l'aide d'une boucle for : \n", "
\n", " for x in X :
\n", "La variable x prendra successivement comme valeur chacun des caractères de X.\n", "

" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": true }, "outputs": [], "source": [ "for x in X : \n", " print(x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

L'opération d'addition permet de concatener les chaines de caractères.

" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "Y=\"Quelle belle chaine de caractères !\"\n", "Z=X+Y\n", "T=X+\"\\n\"+Y#\\n est le caractère de saut de ligne\n", "print(Z)\n", "print(T)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

Ecrivez HA HA HA... HA composée de 1983 HA

" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": true }, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

Informatiquement un caractère est un nombre et tout nombre (inférieur à 1 114 111) est un caractère. On parle du code ASCII du caractère.\n", "

\n", "\n", "
    \n", "
  1. Donner le code ASCII du saut de ligne
  2. \n", "
  3. Donner le code ASCII du caractère 丠
  4. \n", "
  5. Quel caractère a pour code ASCII 666 ?
  6. \n", "
  7. Quel caractère a pour code ASCII de cette année ?
  8. \n", "
\n", "

" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

Ecrire une fonction qui prend en paramètre une chaine de caractère et qui renvoie la liste des codes ASCII des caractères qui la compose

" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def ord_chaine(X) : \n", " return []" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#Test\n", "print(ord_chaine(\"Salut\"))\n", "#Résultat : [83, 97, 108, 117, 116]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

Ecrire une fonction qui prend une liste d'entier correspondant à des codes ASCII et qui renvoie la chaine de caractères de ces codes.

" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def chr_chaine(L) : \n", " return \"\"" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#Test\n", "print(chr_chaine([67, 97, 32, 115, 101, 32, 112, 97, 115, 115, 101, 32, 98, 105, 101, 110, 32, 63]))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

Ecrire la fonction codex qui prend en paramètre un caractère et renvoie 0 si ce caractère est 'A', 1 si c'est 'B', ..., 25 si c'est 'Z' (sans sensibilité à la case) et -1 sinon

" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def codex(cara) : \n", " return -1" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#Tests\n", "print(codex('a'))#0\n", "print(codex('A'))#0\n", "print(codex('e'))#4\n", "print(codex('z'))#25\n", "print(codex('!'))#-1\n", "print(codex('0'))#-1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

Ecrire la fonction xedoc réalisant l'opération inverse. Si l'entier passé en paramètre n'est pas entre 0 et 25 alors la fonction renverra la chaine ?

" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def xedoc(n) : \n", " return '?'" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#Tests\n", "print(xedoc(0))#A\n", "print(xedoc(4))#E\n", "print(xedoc(25))#Z\n", "print(xedoc(26))#?\n", "print(xedoc(-1983))#?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", " Partie 2 : Chiffrement de César \n", "
\n", "\n", "\n", "\n", "

\n", "

\n", " Menu de navigation (cliquez pour dérouler)\n", " \n", "
\n", "

" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

Ecrire la fonction Ecesar(txt, clef) qui réalise un chiffrement de César du texte txt avec la clef clef. On utilisera les fonctions codex et xedoc. En particulier si la chaine txt contient des caractères qui ne sont pas reconnu par la fonction codex alors Ecesar renverra la chaine de caractère vide

" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def Ecesar(txt, clef) : \n", " return \"\"" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#Test\n", "print(Ecesar(\"Bonjour\", 3))#ERQMRXU\n", "print(Ecesar(\"Ca va ?\", 3))# (chaine vide)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

Ecrire la fonction Dcesar(txt, clef) qui déchiffre un message issu d'un chiffrement de César de clef clef. On prendra les même précaution que précédement.

" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def Dcesar(txt, clef) : \n", " return \"\"" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#Test\n", "print(Dcesar(\"IYHCVVUZVJJBWLKLSHAAHXBLTHPUALUHUA\", 7))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

Dans la variable MESSAGE vous retrouverez le message de l'exercice 5 de la feuille de TD. Réaliser une attaque en brute force et déchiffrez ce message.

" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": true }, "outputs": [], "source": [ "MESSAGE = \"VTZTEXKXHNJNHB\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", " Partie 3 : Chiffrement affine \n", "
\n", "\n", "\n", "

\n", "

\n", " Menu de navigation (cliquez pour dérouler)\n", " \n", "
\n", "

" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

Ecrire la fonction Eaffine(txt, clefa, clefb) qui réalise un chiffrement affine du texte txt avec la clef (clefa, clefb). On utilisera les fonctions codex et xedoc. En particulier si la chaine txt contient des caractères qui ne sont pas reconnu par la fonction codex alors Eafine renverra la chaine de caractère vide.

" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def Eaffine(txt, clefa, clefb) : \n", " return \"\"" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#Test\n", "print(Eaffine(\"Salut\", 3, 5))#HFMNK\n", "print(Eaffine(\"Ca va ?\", 3, 5))# (chaine vide)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

Ecrire la fonction inv_mod(a, n) qui renvoie l'inverse de a modulo n. Si l'inversion n'est pas possible la fonction renvera $0$

" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def inv_mod(a, n) :\n", " return 0" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#Tests : il s'agit de l'exercice 6\n", "a=13\n", "n=7\n", "a1=inv_mod(a, n)\n", "if(a1==0) : print(a, \"n'est pas inversible modulo\", n)\n", "else : print(\"L'inverse de\", a, \"modulo\", n, \"est\", a1)\n", " \n", "a=4\n", "n=17\n", "a1=inv_mod(a, n)\n", "if(a1==0) : print(a, \"n'est pas inversible modulo\", n)\n", "else : print(\"L'inverse de\", a, \"modulo\", n, \"est\", a1)\n", " \n", "a=2\n", "n=8\n", "a1=inv_mod(a, n)\n", "if(a1==0) : print(a, \"n'est pas inversible modulo\", n)\n", "else : print(\"L'inverse de\", a, \"modulo\", n, \"est\", a1)\n", " \n", "a=54\n", "n=17\n", "a1=inv_mod(a, n)\n", "if(a1==0) : print(a, \"n'est pas inversible modulo\", n)\n", "else : print(\"L'inverse de\", a, \"modulo\", n, \"est\", a1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

Ecrire la fonction Daffine(txt, clefa, clefb) qui réalise un déchiffrement affine.

" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def Daffine(txt, clefa, clefb) : \n", " return \"\"" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#Test\n", "print(Daffine(\"CONQMNVGBORMDZOBUOKWVGRQMBODWD\", 19, 16))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", " Partie 4 : L'encodage\n", "
\n", "\n", "

\n", "

\n", " Menu de navigation (cliquez pour dérouler)\n", " \n", "
\n", "

" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

Comme nous l'avons vu, un caractère est en fait une valeur numérique, mais il existe plusieurs manières de considérer ces nombres. On parle de l'encodage d'un caractère. L'encodage le plus courrant est UTF8 (surtout pour le web) mais il y a également l'ANSI, l'ASCII ou le LATIN1. Les méthodes encode et decode des chaines de caractères permettent de spécifier les encodages. \n", "

\n", "

" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "txt=\"Il était une fois...\"\n", "print(txt)\n", "print(txt.encode(\"utf-8\"))\n", "print(txt.encode(\"latin1\"))\n", "print(\"\\n\")\n", "\n", "txt=\"Ben ça alors !\".encode(\"utf-8\")\n", "print(txt)\n", "print(type(txt))\n", "print(txt.decode())#La vleur par défaut est celle de jupyter = celle du navigateur - en générale UTF8\n", "print(txt.decode(\"ascii\"))\n", "#Cette dernière instruction va provoquer une erreur \n", "#car le caractère 'ç' encodé en utf-8 par le nombre hexadécimale c3, c'est à dire la valeur 195 (caractère \\xc3),\n", "#ne se décode pas en en ascii" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

Ecrire une fonction qui prend en paramètre une chaine de caractère encodée en UTF8 et qui renvoie cette chaine encoder en ANSI

" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def modif_encode(txt) : \n", " return txt" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "txt = \"Ma chaîne !\"\n", "print(txt)#Ma chaîne !\n", "print(modif_encode(txt))#Ma chaîne !" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

Dans la suite nous allons faire du chiffrement de la vie de tous les jours. Dans le cadre actuel, un chiffrement affecte l'expression binaire d'une donnée. On considèrera alors que tous nos messages (c'est à dire les variables d'entrée et de sortie de nos fonctions de chiffrement) seront de telles expressions binaires en suivant l'encodade le plus répandu : utf-8. \n", "

Pour simplifier l'expression des chaînes de caractères nous allons les écrires en hexadécimale. \n", "
L'hexadécimal est le système de numération en base 16 : 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F. Un nombre en hexadécimale se repère en python par 0x devant son expression.

" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "for n in range(33) : print(n, \"=\", hex(n))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

La méthode éponyme, transforme une chaine binaire en hexadécimale

" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "X=b'Salut'\n", "print(X.hex())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

La fonction bytes.fromhex transforme une chaine exprimée en hexadécimale en binaire.

" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "X='436f75636f75'\n", "print(bytes.fromhex(X))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

Ecrire la fonction str2hex qui prend en paramètre une chaine de caractère et renvoie son expression hexadécimale

" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def str2hex(txt, encodage=\"utf-8\") : \n", " return " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "txt=\"Quelle galère ces histoires d'encodage !\"\n", "print(str2hex(txt))#5175656c6c652067616cc3a872652063657320686973746f69726573206427656e636f646167652021" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

Ecrire la fonction hex2str réalisant le processus inverse.

" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def hex2str(X, encodage=\"utf-8\") : \n", " return " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#Test\n", "X='4f6e20766f697420656e66696e206c6520626f75742064652063652062617a617264'\n", "print(hex2str(X))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", " Partie 5 : Chiffrement AES \n", "
\n", "\n", "\n", "\n", "

\n", "

\n", " Menu de navigation (cliquez pour dérouler)\n", " \n", "
\n", "

" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

Le protocole AES est un peu ardu a expliquer du coté mathématiques de la force : corps fini, groupe de galois, ensemble de permutation, et tout plein d'autre joyeuseté. Par chance, il existe une bibliothèque qui s'occupe de tout ça : cryptography (et pas que de AES, aussi beaucoup d'autre protocole de chiffrement).

\n", "\n", "

Voyons pas à pas comment l'utliser pour réaliser un chiffrement AES.

" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Etape 1 : Importer les modules de cette bibliothèque pour réaliser un chiffrement AES." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes\n", "from cryptography.hazmat.backends import default_backend\n", "from cryptography.hazmat.primitives import padding\n", "import os" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Etape 2 : Générer une clef qui faudra conserver et communiquer (très) secretement aux personnes à qui sont destinés les messages cryptés." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "clef = os.urandom(16).hex()#Clef sur 16 octets = 128 bits donner en binaire exprimer en hexadécimale" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(clef)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Etape 3 : Créer un objet Cipher (chiffrement en anglais) qui contient en quelque sorte tout le mecanisme permettant de chiffrer (et déchiffrer) avec AES." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "cipher = Cipher(algorithms.AES(bytes.fromhex(clef)), modes.CBC(b\"\\0\"*16), backend=default_backend())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

Cet objet de chiffrement (Cipher) prend deux paramètres : le premier est le protocole. Dans notre cas c'est le chiffrement AES avec sa clef (qui doit être exprimée en binaire, d'où l'appel de la fonction bytes.fromhex). Le second est le mode de chiffrement. Celui que nous avons choisi ici est le mode CBC pour Cipher-Block Chaining.\n", " Il en existe d'autre :\n", "

\n", "qui indique à l'algorithme la méthode de chiffrement. Dans le cadre du CBC, le message est découpé en bloc de 128 bits et chaque bloc du message, avec une opération sur la clef, sert de clef au bloc suivant. Cela impose deux contraintes\n", "
    \n", "
  1. Il faut avoir un \"bloc initial\" pour lancer l'itération du processus. C'est le paramètre pris par la méthode CBC, précisément le vecteur nul sur 16 octets : b\"\\0\"*16. Il est possible de mettre n'importe quelle valeur (sur 16 octets) mais dans ce cas, cette valeurs doit également être transmise avec la clef.\n", "
  2. \n", "
  3. Il faut que le texte (également en binaire) à crypter soit multiple de 128 bits sinon cela provoquera une erreur. Il existe (décidement quelle chance !) une fonction qui rajoute les bits nécessaires.
  4. \n", "
\n", "

" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "txt = \"C'est aussi facile que ça de faire un chiffrement AES ?\"\n", "\n", "#On le converti en hexadécimale car c'est la convention de ce TP : \n", "txt_hex = str2hex(txt)\n", "\n", "#transformation de la chaine de caractère en binaire\n", "txt_binaire = bytes.fromhex(txt_hex)\n", "\n", "#Gestion de la taille du message\n", "padder = padding.PKCS7(128).padder()\n", "txt_pret_a_crypter = padder.update(txt_binaire) + padder.finalize()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Etape 4 : Avoir un objet pour crypter ou décrypter." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "crypter = cipher.encryptor() #Servira pour le chiffrement\n", "decrypter = cipher.decryptor() #Servira pour le déchiffrement" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Etape 5 : S'amuser ! " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#Chiffrement\n", "txt_crypter = crypter.update(txt_pret_a_crypter) + crypter.finalize()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(\"Texte claire\\n\", txt, end=\"\\n\\n\")\n", "print(\"Clef\\n\", clef, end=\"\\n\\n\")\n", "print(\"Texte crypté en binaire\\n\", txt_crypter, end=\"\\n\\n\")\n", "print(\"Texte crypté en hexadécimal\\n\", txt_crypter.hex(), end=\"\\n\\n\")\n", "print(\"Texte en langage courrant\\n\", hex2str(txt_crypter.hex(), \"latin1\"))\n", "#En choissisant l'encodage latin1, on evite des erreurs du aux limites de l'utf-8. \n", "#mais certains caractères ne seront pas reconnu par ce navigateur" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

\n", "Pour déchiffrer, c'est la même chose... mais pas dans le même ordre.\n", "

" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#Données\n", "mess_crypt_hex = '40dd9836ded8feb212d0a40000552320b94f12509f95d6958af70604b919df799cefdee20b9b04861727139647ff731d0e9356459d9896a6346513b39ac7b2555110a2477a13f8750d388c4361b4ea3c'\n", "clef_hex = 'dd19bb5b21dfa8050bad13bb4738a21a'\n", "\n", "#Expression binaire\n", "mess_crypt_bin = bytes.fromhex(mess_crypt_hex)\n", "clef_bin = bytes.fromhex(clef_hex)\n", "\n", "#Objet pour le déchiffrement\n", "cipher = Cipher(algorithms.AES(clef_bin), modes.CBC(b\"\\0\"*16), backend=default_backend())\n", "decrypter = cipher.decryptor()\n", "\n", "#Déchiffrement\n", "mess_decrypt_bin = decrypter.update(mess_crypt_bin) + decrypter.finalize()\n", "\n", "#Suppression de caractère en fin de chaine (qui suivent le remplissage PKCS7 sur 16 octets = 128 bits)\n", "unpadder = padding.PKCS7(128).unpadder()\n", "mess_decrypt_bin = unpadder.update(mess_decrypt_bin) + unpadder.finalize()\n", "\n", "#expression hexadécimale\n", "mess_decrypt_hex=mess_decrypt_bin.hex()\n", "\n", "print(\"Message décrypté en hexadécimal :\", mess_decrypt_hex, end=\"\\n\\n\")\n", "print(\"Message décrypté (pour humain) :\", hex2str(mess_decrypt_hex))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

Ecrire les fonctions E_AES(message, clef) et D_AES(message, clef) réalisant chiffrement et déchiffrement AES tel que décrit ci-dessus. Les données en entrées et en sorties sont toutes en hexadécimale.

" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def E_AES(message, clef) :\n", " return message\n", "\n", "def D_AES(message, clef) :\n", " return message" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#Tests\n", "clef = os.urandom(16).hex()\n", "txt = \"Alors ça va les gens ? Ca crypte et décrypte bien ?\"\n", "txt0 = str2hex(txt)\n", "txt1 = E_AES(txt0, clef)\n", "txt2 = D_AES(txt1, clef)\n", "print(\"Message :\", txt)\n", "print(\"\\nEn hexa :\", txt0)\n", "print(\"\\nClef :\", clef)\n", "print(\"\\nMessage crypté :\", txt1)\n", "print(\"\\nMessage claire :\", txt2)\n", "print(\"\\nMessage claire :\", hex2str(txt2))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", " Partie 6 : Hacher\n", "
\n", "\n", "\n", "

\n", "

\n", " Menu de navigation (cliquez pour dérouler)\n", " \n", "
\n", "

" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

Comme nous l'avons évoquer en cours, utiliser une fonction bijective, c'est crypter. Moralement cela signifie que l'on peut décrypter. Hacher, c'est le même principe : on applique une fonction (injective et non bijective). On obtient ce que l'on appel une empreinte. Dire que la fonction utilisée est injective signifie que si deux messages donnent la même empreinte alors les messages sont nécessairement identique. La différence est qu'il sera (pratiquement) impossible de récupérer le message claire. Le hashage permet par exemple de stocker des mots de passe dans une base de donnée. Il n'est pas nécessaire d'être capable de les décrypter (c'est d'ailleur plus morale de ne pas être capable de les décrypter), il suffit d'être capable de vérifier que le mot de passe saisi a la même empreinte que celle stockée dans la base de donnée.

" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

Bien que la bibliothèque cryptography permette de gérer quelques protocoles de hachage, la bibliothèque haslib permet la même chose bien plus facilement.

" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from hashlib import *" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

Cette bibliothèque permet d'utiliser de nombreuse fonction de hachage bien connue : \n", "

\n", "Appelons l'une de ces fonction f_hash. Alors pour hacher une chaine de caractère txt, exprimée en bianire, on peut faire f_hash(txt).hexdigest(). La méthode hexdigest() permet d'afficher l'empreinte en hexadécimale.\n", "

" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

Réaliser le hachage des chaines salut (deux espaces à la fin) et salut ! (un espace et un point d'exclamation à la fin) correspondant respectivement aux codes héxadécimaux 73616c75742020 et 73616c75742021, en suivant les protocoles sha1, sha256, sha512, md5, sha3_256, sha3_512

" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": false }, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

Pourquoi tant de fonction différente ? La raison est simple : ces fonctions ne sont pas réellement injective. Elles le sont presque. En effet, on a longtemps pensé par exemple que md5 était une bonne fonction de hashage. Cependant, il a été possible de démontrer qu'il existait une infinité de collision. Une collision c'est lorsque l'on trouve deux messages différents qui ont la même empreinte. Observez une collision avec les deux chaines suivantes après avoir vérifié qu'elles sont bien différentes :

\n", "d131dd02c5e6eec4693d9a0698aff95c2fcab58712467eab4004583eb8fb7f89 \n", "55ad340609f4b30283e488832571415a085125e8f7cdc99fd91dbdf280373c5b \n", "d8823e3156348f5bae6dacd436c919c6dd53e2b487da03fd02396306d248cda0 \n", "e99f33420f577ee8ce54b67080a80d1ec69821bcb6a8839396f9652b6ff72a70\n", "

\n", "d131dd02c5e6eec4693d9a0698aff95c2fcab50712467eab4004583eb8fb7f89 \n", "55ad340609f4b30283e4888325f1415a085125e8f7cdc99fd91dbd7280373c5b \n", "d8823e3156348f5bae6dacd436c919c6dd53e23487da03fd02396306d248cda0 \n", "e99f33420f577ee8ce54b67080280d1ec69821bcb6a8839396f965ab6ff72a70\n", "\n", " \n", "

" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", " Partie 7 : RSA\n", "
\n", "\n", "

\n", "

\n", " Menu de navigation (cliquez pour dérouler)\n", " \n", "
\n", "

" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

\n", "La bibliothèque cryptography gère également le protocole de chiffrement RSA.\n", "
\n", "La première étape consiste à créer un jeu de clef publique et de clef privé. Pour y arriver, rien de plus simple, il suffit d'utiliser la fonction generate_private_key qui prend en paramètre la puissance que l'on souhaite considérer (en général on met $3$ ou $65537$, c'est une convention dans ce protocole, le chiffrement est alors tout aussi sécurisé et bien rapide). Ainsi que la taille de la clef souhaité, en générale $2048$, mais on peut aussi prendre $4096$ (prend du temps en calcul) ou $1024$ (racourci le temps de calcul mais augmente la vulnérabilité tout comme le $512$ que nous essayerons d'attaquer à la fin de ce TP).\n", "
\n", " Il est alors possible de récupérer toutes les données qui permettent de la construction des clefs (voir cours)\n", "

" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from cryptography.hazmat.primitives.asymmetric.rsa import *\n", "from cryptography.hazmat.primitives.asymmetric import *\n", "\n", "# Génération de la clé privée\n", "PR = generate_private_key(public_exponent=3, key_size=2048)\n", "PU = PR.public_key()\n", "\n", "# On peut avoir accès à toutes les données permettant de créer une clef RSA : \n", "p=PR.private_numbers().p\n", "q=PR.private_numbers().q\n", "d=PR.private_numbers().d\n", "n=PU.public_numbers().n\n", "e=PU.public_numbers().e\n", "\n", "print(\"p =\", p, end=\"\\n\\n\")\n", "print(\"q =\", q, end=\"\\n\\n\")\n", "print(\"e =\", e, end=\"\\n\\n\")\n", "print(\"d =\", d, end=\"\\n\\n\")\n", "\n", "print(\"p*q =\", p*q, end=\"\\n\\n\")\n", "print(\"n =\", n, end=\"\\n\\n\")\n", "print(\"Est-ce que p*q=n ? Réponse :\", p*q==n, end=\"\\n\\n\")\n", "print(\"phi =\", (p-1)*(q-1), end=\"\\n\\n\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

Ecrire une fonction qui prend en paramètre, $p$, $q$ et $e$ et retourne le $d$ (voir cours)

" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def trouve_d(p, q, e) : \n", " return 0" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#Test\n", "p=23\n", "q=47\n", "e=3\n", "print(\"d =\", trouve_d(p, q, e))# d = 675" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

Générer une clef RSA et vérifier que le $d$ est bien le même que celui trouvé par la fonction précédente.

" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

Pour crypter un message : \n", "
\n", " PU.encrypt(message, asymmetric.padding.OAEP(mgf=asymmetric.padding.MGF1(algorithm=hashes.SHA1()), algorithm=hashes.SHA1(), label=None))
\n", " et pour décrypter :
\n", " PR.decrypt(message, asymmetric.padding.OAEP(mgf=asymmetric.padding.MGF1(algorithm=hashes.SHA1()), algorithm=hashes.SHA1(), label=None))
\n", " où PU est la clef publique et PR la clef privé et où le message est exprimé en binaire\n", "

" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Génération des clés\n", "PR = generate_private_key(public_exponent=3, key_size=2048)\n", "PU = PR.public_key()\n", "\n", "# Chiffrement du message\n", "message = \"Voilà un chiffrement RSA !\"\n", "message_hex = str2hex(message)\n", "message_crypt_bin = PU.encrypt(bytes.fromhex(message_hex), padding.OAEP(mgf=padding.MGF1(algorithm=hashes.SHA1()), algorithm=hashes.SHA1(), label=None))\n", "message_crypt_hex = message_crypt_bin.hex()\n", "\n", "# Déchiffrement du message\n", "message_decrypt_bin = PR.decrypt(message_crypt_bin, padding.OAEP(mgf=padding.MGF1(algorithm=hashes.SHA1()), algorithm=hashes.SHA1(), label=None))\n", "message_decrypt = hex2str(message_decrypt_bin.hex())\n", "\n", "# affichage\n", "print(\"Message crypté en hexadécimale :\", message_crypt_hex)\n", "print(\"\\nMessage décrypté :\", message_decrypt)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

Si vous connaissez une clef publique, vous pouvez créer un objet, au sens de la bibliothèque cryptography pour réaliser un chiffrement.
Pour cela on utilise la fonction RSAPublicNumbers(e, n) qui prend les paramètres de la clef publique. A partir de cette objet, on peux chiffrer comme dans l'exemple suivant. Si le $n$ ou le $e$ ne sont pas bien construit cela provoquera une erreur.

" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "n = 29291407906661569472287222393914988561121346224578366096956865504069610510947132825898478561682959684868270319259430528986547123478512425513436561435951536755144380691346255135695410065529892811623664119647430390772008241917984594572488566822493466512734047523563651548834915033917693981551520239544232568044932401930893874633926806517969198644905026004428677805143348187011317821797600408602342806098852455207716990486333961685138915418934363171978118155029886241781164613625729794217882737850748734159261155665379041273070688752419875667059212663655098996353227324559813058604901660148345909423583115198969800802389\n", "e = 3\n", "\n", "nombres_publique = RSAPublicNumbers(e, n)\n", "PU = nombres_publique.public_key(backend=default_backend())\n", "\n", "message = \"Test\"\n", "message_hex = str2hex(message)\n", "message_crypt_bin = PU.encrypt(bytes.fromhex(message_hex), padding.OAEP(mgf=padding.MGF1(algorithm=hashes.SHA1()), algorithm=hashes.SHA1(), label=None))\n", "message_crypt_hex = message_crypt_bin.hex()\n", "\n", "print(\"Message claire :\\n\", message)\n", "print(\"Message claire (hex) :\\n\", message_hex)\n", "print(\"Message crypté (hex) :\\n\", message_crypt_hex)\n", "print(\"Message crypté :\\n\", hex2str(message_crypt_hex, \"latin1\"))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

Ecrire une fonction E_RSA(message, n, e) qui prend en paramètre les éléments d'une clef publique et un message en hexadécimal et qui réalise un chiffrement RSA. La valeur de retour est la chaine de caractère chiffré exprimé en hexadécimal.

" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def E_RSA(txt, n, e) :\n", " return txt" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#Test\n", "n = 29291407906661569472287222393914988561121346224578366096956865504069610510947132825898478561682959684868270319259430528986547123478512425513436561435951536755144380691346255135695410065529892811623664119647430390772008241917984594572488566822493466512734047523563651548834915033917693981551520239544232568044932401930893874633926806517969198644905026004428677805143348187011317821797600408602342806098852455207716990486333961685138915418934363171978118155029886241781164613625729794217882737850748734159261155665379041273070688752419875667059212663655098996353227324559813058604901660148345909423583115198969800802389\n", "e = 3\n", "message=\"Tout roule ;-)\"\n", "txt=E_RSA(str2hex(message), n, e)\n", "\n", "print(\"Message claire :\\n\", message)\n", "print(\"Message crypté (hex) :\\n\", txt)\n", "print(\"Message crypté :\\n\", hex2str(txt, \"latin1\"))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

Inversement, si nous connaissons la clef privé, nous pouvons faire un déchiffrement. De manière duale, on utilise la fonction RSAPrivateNumbers mais cette fonction a besoin de plus que le $n$ et le $d$. Il faut en effet lui passer en paramètre le $p$ et $q$ tel que $n=p\\times q$. Le propriétaire de la clef privé étant le seul a avoir besoin de connaitre ce secret ce n'est pas un problème de sécurité. Il se trouve que cette fonction prend également d'autre paramètre mais ils ne dépendent que de $p$, $q$ et $d$.\n", "

" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "p=152138309605250969672678306100565819678458264275759740547716653967057595647585571162766357901741902140485755558123984288337553033889756973760407872301762351970190785193238467642351494069240811939857897711505390256289406682129447021040918320275827237346392789804770754724785475215927722116744939661244873302163\n", "q=145759945113837957225843299399854376920280571238474213176341612517657496026703145187769482325849050676945793827707837057473827025994893297255196085656346385262710600088566656583910515475165990020169452504731083699265770872495624156338521866343192105731424979698742173437499434744923835916374934727560785142047\n", "d=14783781105182311621293948377389820757773350553319879862357233797436830639686916963034983907800963112202779530977619299911891609126004284850867017686691836972299633932325621141152036440181841721803875434495848657796342838659793713767487902116459815470731415256278274246739028666979886768159510615814714616359342055206692025014147264699966541463823898942823315050070249147318376442502506736822362761390381041785788576270519430176658873918741196894870584270624069929298765227604003742945561753538380845141388283280293073795551638647444676450571263641834171696898876939567657506331911062242081256780402250477460165935635\n", "\n", "nombres_publique = RSAPublicNumbers(inv_mod(d, (p-1)*(q-1)), p*q)\n", "PR = RSAPrivateNumbers(p, q, d, d % (p-1), d % (q-1), inv_mod(q,p), nombres_publique).private_key()\n", "\n", "message = '503fd954df2490fc3f14179637848538584f49e548cab7f72c5e6d38d7887a09563e47372758e2f7705e99d350075f3ff3927184e18a941f6e6bddec89d206c65ce50279eee97b3b6a21f116170331c569f39a1e58034b5eda51500611a5ec61ccc0b61bbf9af387eb6d035f4243d9aa575ffc0e8f18578e8eeff52a60d9eeff33d31a73f8c88dd9f5c18671e4e79da5408ac058124a19ec0b320536896740b434e66672c011bce7989c46ab56d0fe136a85dd14e266d092f3c8e8f5551312d037b9477ecb652264e84f13f68bd51c2f282956d632297d335097a18fe070af774df2270ad417d6ebf9c0e7ff17ff36e353819296bc05a2de089ad83a1a15bfd0'\n", "message_decrypt_bin = PR.decrypt(bytes.fromhex(message), padding.OAEP(mgf=padding.MGF1(algorithm=hashes.SHA1()), algorithm=hashes.SHA1(), label=None))\n", "message_decrypt_hex = message_decrypt_bin.hex()\n", "\n", "print(\"Message claire (héxa) :\", message_decrypt_hex)\n", "print(\"Message claire :\", hex2str(message_decrypt_hex))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

Ecrire une fonction D_RSA(message, p, q, d) qui prend en paramètre les éléments d'une clef privé et un message en hexadécimal et qui réalise un déchiffrement RSA. La valeur de retour est la chaine de caractère chiffré exprimé en hexadécimal.

" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def D_RSA(txt, p, q, d) :\n", " return txt" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#Test\n", "p = 12988454751701541974107348931475545120050312831239160253861806208350206227205166449972260776014779007222046273974869771991296181834674834159856067718114333\n", "q = 10784149788394631686981335656765118930029082756451536184056073534927579783276193203351746561675367435701379839730452707937004318383251663806705463399599619\n", "d = 93379627708090288219525062080520560199412478883820724433170376313207731724716544340241788723147562988307030529154886090254813309375181350726599471987902341675556873680282573386711442366910386210511793010201631141787839897781723872277995688698878380330099961238236357217940551108677979684484852090889831683451\n", "txt = '0fd8e1d3d1ef2f5b6d2ac8bf1416a751fcd167b13496b2d267b9edf384d280265a648749bd552e246fb18b9e2b94de79d669648725920c94ec91904a1dcb6d595ca63feb28e53fa32d2df30da1b7405e6b0c3a3236e12f46a2eea134a3b84701f35bd8d9eb6904acadd04f185fe6c027cea809bb8a6ac81b874073ea9f24fb8a'\n", "\n", "print(\"Message claire :\", hex2str(D_RSA(txt, p, q, d)))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

Voulez-vous connaitre un terrible secret de prof ? Ma clef publique est
\n", "\n", " n= 1510618632668270646714141761954708577877544378205\n", " 7320975339456471343619842110692441380213747657509282\n", " 3651759293886518958232115577497523602040649134713228\n", " 0398515972165561655713266308748142512846066559556799\n", " 6605379872995602094033482505223280967401296724886969\n", " 8707157009856428482116212906892906057970523319569243\n", "\n", "
\n", " e = 65537\n", "
et vous avez appris, petits espions que vous êtes, que $n=p\\times q$ et $p+q$ est compris entre \n", "\n", " 2464104660488624191280708373293\n", " 0419471753688886381369404785220\n", " 6660120754633373306525629555412\n", " 4697859073628721712173770897450\n", " 8169412098556466656647930327021\n", "\n", "et\n", "\n", " 2464104660488624191280708373293\n", " 0419471753688886381369404785220\n", " 6660120754633373306525629555412\n", " 4697859073628721712173770897450\n", " 8169412098556466656647943327996\n", "\n", "\n", "Voici le message qu'il vous faut essayer de déchiffrer (si vous le pouvez ... pauvre humain) : \n", "\n", " cd82c20ee60d47eb69d5a9f47db6a604\n", " 254f4414f19ff38dc13a0a79e4353b57\n", " ec3227d547bf7d00ad4474b8a8b9ddc4\n", " 72b7f6c90d6b8557515c163a31014e09\n", " 52c1926fdd6801a53ed40f9ec180a906\n", " 4cf3a676a341a0b99781652d4b50b2e1\n", " f3b0c34cdbd0968ab062bcbe90c97a34\n", " 913907ffa643080ca961bb5ac60e26d1\n", "\n", "

" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "hide_input": false, "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.0" } }, "nbformat": 4, "nbformat_minor": 2 }